package com.mixsmart.utils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.mixsmart.constant.IMixConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URLDecoder;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 简单对象工具类
 * @author 乌草坡
 * @since 1.0
 */
public class ObjectUtils {

	private static final Logger logger = LoggerFactory.getLogger(ObjectUtils.class);

	/**
	 * 根据内容进行数据类型转换
	 * <p>如:字符串“5”转换之后变成整型5</p>
	 * @param value 数据类型转换的值
	 * @return 转换后的值
	 */
	public static Object convertDataType(Object value) {
		Object obj = null;
		String valueStr = value.toString();
		if(StringUtils.isNotEmpty(valueStr)) {
			if(StringUtils.isInteger(valueStr)) {
				obj = Integer.parseInt(valueStr);
			} else if(StringUtils.isDecimal(valueStr)) {
				obj = Double.parseDouble(valueStr);
			} else {
				obj = value;
			}
		} else {
			obj = value;
		}
		return obj;
	}
	
	/**
	 * boolean型转化为整型 <br />
	 * 转换依据是:true--1;false--0
	 * @param value 需要处理的值
	 * @return 返回1或0
	 */
	public static int boolean2Int(boolean value) {
		return value?1:0;
	}
	
	
	/**
	 * boolean型转化为String <br />
	 * 转换依据是:true--"1";false--"0"
	 * @param value 需要处理的值
	 * @return 返回“1”或“0”
	 */
	public static String boolean2String(boolean value) {
		return value?"1":"0";
	}
	
	
	/**
	 * int型转化为boolean <br />
	 * 转换依据是:1--true;非1--false
	 * @param value 需要处理的值
	 * @return 返回true或false
	 */
	public static boolean int2Boolean(int value) {
		return value == 1;
	}
	
	
	/**
	 * String型转化为boolean <br />
	 * 转换依据是:"1"--true；非"1"--false
	 * @param value 需要处理的值
	 * @return 返回true或false
	 */
	public static boolean string2Boolean(String value) {
		return "1".equals(value);
	}
	
	/**
	 * 简单Bean对象转换为Map对象；
	 * <p>转换规则是：实例属性作为Map的key，实例属性对应的值作为Map对象的value.</p>
	 * <p>注：当属性对应的值为空（null或""）时，实例对应的属性不会放到Map对象中；既：属性对应的值为空时不转换.</p>
	 * @param objBean 简单对象实例
	 * @return 返回转换后的Map对象
	 */
	public static Map<String, Object> simpleBean2Map(Object objBean) {
	    if(null == objBean) {
	        return null;
	    }
	    Class<?> clazz = objBean.getClass();
	    Map<String, Object> maps = new HashMap<>();
	    convert2Map(objBean, clazz, maps, false);
	    return (maps.isEmpty() ? null : maps);
	}

	/**
	 * 简单Bean对象转换为Map对象；
	 * <p>转换规则是：实例属性作为Map的key，实例属性对应的值作为Map对象的value.</p>
	 * <p>注：当属性对应的值为空（null或""）时，实例对应的属性不会放到Map对象中；既：属性对应的值为空时不转换.</p>
	 * @param objBean 简单对象实例
	 * @param autoConvertMultivalued 是否自动转换为多值方式
	 * @return 返回转换后的Map对象
	 */
	public static Map<String, Object> simpleBean2Map(Object objBean, boolean autoConvertMultivalued) {
		if(null == objBean) {
			return null;
		}
		Class<?> clazz = objBean.getClass();
		Map<String, Object> maps = new HashMap<>();
		convert2Map(objBean, clazz, maps, autoConvertMultivalued);
		return (maps.isEmpty() ? null : maps);
	}
	
	/**
	 * 对象转换为Map
	 * @param objBean 对象实例
	 * @param clazz 对象类
	 * @param maps 转换后的Map对象
	 * @param autoConvertMultivalued 是否自动转换为多值方式
	 */
	private static void convert2Map(Object objBean, Class<?> clazz, Map<String, Object> maps, boolean autoConvertMultivalued) {
	    Field[] fields = clazz.getDeclaredFields();
        if(fields.length == 0) {
			Class<?> superClazz = clazz.getSuperclass();
			if (null != superClazz
					&& !superClazz.getName().equals("java.lang.Object")
					&& !superClazz.isInterface()) {
				convert2Map(objBean, superClazz, maps, autoConvertMultivalued);
			} else {
				return;
			}
        }
        try {
            for(Field field : fields) {
                if(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                    continue;
                }
                String fieldName = field.getName();
                PropertyDescriptor propertyDesc = new PropertyDescriptor(fieldName, clazz);
                Method method = propertyDesc.getReadMethod();
                if(null != method) {
                    Object value = method.invoke(objBean);
                    if(null != value && StringUtils.isNotEmpty(value.toString())) {
                        if(value instanceof String && value.toString().startsWith("%")) {
                        	String valueStr = URLDecoder.decode(value.toString(), "UTF-8");
                        	Object newValue = valueStr;
                        	if(valueStr.contains(IMixConstant.MULTI_VALUE_SPLIT) && autoConvertMultivalued) {
                        		newValue = valueStr.split(IMixConstant.MULTI_VALUE_SPLIT);
							}
                            maps.put(fieldName, newValue);
                        } else if(value.getClass().isArray()){
							assert value instanceof Object[];
							Object[] values = (Object[])value;
                            if(values.length > 0 ) {
                                maps.put(fieldName, value);
                            }
                        } else {
                        	if(value instanceof String) {
                        		String valueStr = value.toString();
								Object newValue = valueStr;
								if(valueStr.contains(IMixConstant.MULTI_VALUE_SPLIT) && autoConvertMultivalued) {
									newValue = valueStr.split(IMixConstant.MULTI_VALUE_SPLIT);
								}
								maps.put(fieldName, newValue);
							} else {
								maps.put(fieldName, value);
							}
                        }
                    }
                }
            }
        } catch (Exception e) {
            LoggerUtils.error(logger, e);
        }
		Class<?> superClazz = clazz.getSuperclass();
        if (null != superClazz
				&& !superClazz.getName().equals("java.lang.Object")
				&& !superClazz.isInterface()) {
            convert2Map(objBean, superClazz, maps, autoConvertMultivalued);
        }
	}
	
	/**
	 * 处理Boolean类型数据；对象转Boolean类型
	 * @param value 值
	 * @return 返回true或false
	 */
	public static boolean handleBoolean(Object value) {
        if(null == value) {
            return false;
        }
        try {
            return Boolean.parseBoolean(value.toString());
        } catch (Exception e) {
            return false;
        }
    }

	/**
	 * 获取单个值；如果value参数是数组或列表，则处理后返回的值为第一个值；否则返回value本身
	 * @param value 需要处理的值（可能是数组、列表或字符串、整型等）
	 * @return 返回处理后的值；
	 */
	public static Object getSingleValue(Object value) {
		return handleMultiValue(value, 0);
	}

	/**
	 * 处理多值；
	 * @param value 值
	 * @param index 需要获取的下标；如果value是一个数组或列表时
	 * @return 返回处理后的值
	 */
	public static Object handleMultiValue(Object value, int index) {
		return handleMultiValue(value, index, true);
	}

	/**
	 * 处理多值；
	 * @param value 值
	 * @param index 需要获取的下标；如果value是一个数组或列表时
	 * @param enableSplitStr 有能力拆分字符串（按英文逗号拆分）；
	 * @return 返回处理后的值
	 */
	public static Object handleMultiValue(Object value, int index, boolean enableSplitStr) {
		if(null == value) {
			return null;
		}
		if(index == -1) {
			return value;
		}
		if(index < -1) {
			return null;
		}
		Object handleValue = value;
		if(value.getClass().isArray()) {
			String arrayStr = JSONUtils.toJson(value);
			List<Object> list = JSONUtils.fromJson(arrayStr, new TypeReference<List<Object>>() {});
			handleValue = handleValueArray(list.toArray(), index);
		} else if(value instanceof List) {
			List list = (List)value;
			if(index < list.size()) {
				handleValue = list.get(index);
			} else {
				handleValue = null;//list.getFirst();
			}
		} else if(value instanceof Set) {
			Set sets = (Set)value;
			handleValue = handleValueArray(sets.toArray(), index);
		} else if(value instanceof String) {
			String valueStr = StringUtils.handleNull(value);
			if(enableSplitStr) {
				if(valueStr.contains(IMixConstant.MULTI_VALUE_SPLIT)) {
					String[] array = StringUtils.stringToArray(valueStr);
					handleValue = handleValueArray(array, index);
				} else {
					handleValue = valueStr;
				}
			}
		}
		return handleValue;
	}

	private static Object handleValueArray(Object[] array, int index) {
		if(index < array.length) {
			return array[index];
		}
        return null;
	}


	/**
	 * 转换对象
	 * @param convertedClazz 需要转换成的类类型
	 * @param instance 需要转换的对象
	 * @param <E> 类型
	 * @return 返回对应类型的对象
	 */
	public static <E> E convertObject(Class<E> convertedClazz, Object instance) {
		return convertObject(convertedClazz, instance, false);
	}

	/**
	 * 转换对象
	 * @param convertedClazz 需要转换成的类类型
	 * @param instance 需要转换的对象
	 * @param <E> 类型
	 * @return 返回对应类型的对象
	 */
	public static <E> E convertObject(Class<E> convertedClazz, Object instance, boolean isIgnoreNullValue) {
		if(null == convertedClazz || null == instance) {
			return null;
		}
		E e = null;
		try {
			e = convertedClazz.getDeclaredConstructor().newInstance();
			setProperties(instance, e, BeanUtils.getPropertyDescriptors(instance.getClass()),
					BeanUtils.getPropertyDescriptors(convertedClazz), isIgnoreNullValue);
		} catch (IllegalAccessException | InstantiationException ex) {
			LoggerUtils.error(logger, ex);
		} catch (InvocationTargetException | NoSuchMethodException ex) {
            throw new RuntimeException(ex);
        }
        return e;
	}

	/**
	 * 获取目标类的属性描述对象；如果没有找到则返回null
	 * @param targetPropertyDescriptors 目标类的属性描述数组
	 * @param propertyDesc 属性描述对象
	 * @return 如果存在返回属性描述对象；否则返回null
	 */
	private static PropertyDescriptor getTargetPropertyDescriptor(PropertyDescriptor[] targetPropertyDescriptors, PropertyDescriptor propertyDesc) {
		String fieldName = propertyDesc.getName();
		if(StringUtils.isEmpty(fieldName)) {
			return null;
		}
		return Arrays.stream(targetPropertyDescriptors).filter(pd -> {
			String tFieldName = pd.getName();
			if(fieldName.equals(tFieldName)) {
				return null != pd.getWriteMethod();
			}
			return false;
		}).findAny().orElse(null);
	}

	/**
	 * 重新赋值
	 * @param fromSource 源对象（从哪个对象）
	 * @param toTarget 目标对象（赋值到哪个对象）
	 */
	public static void reassign(Object fromSource, Object toTarget) {
		if(null == fromSource || null == toTarget) {
			return;
		}
		setProperties(fromSource, toTarget, BeanUtils.getPropertyDescriptors(fromSource.getClass()),
				BeanUtils.getPropertyDescriptors(toTarget.getClass()), false);
	}

	/**
	 * 获取属性描述数组
	 * @param clazz 类类型
	 * @return 返回属性描述数组
	 */
	public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) {
		return BeanUtils.getPropertyDescriptors(clazz);
	}

	/**
	 * 重新赋值
	 * @param fromSource 源对象（从哪个对象）
	 * @param toTarget 目标对象（赋值到哪个对象）
	 */
	public static void reassign(Object fromSource, Object toTarget, boolean isIgnoreNullValue) {
		if(null == fromSource || null == toTarget) {
			return;
		}
		setProperties(fromSource, toTarget, BeanUtils.getPropertyDescriptors(fromSource.getClass()),
				BeanUtils.getPropertyDescriptors(toTarget.getClass()), isIgnoreNullValue);
	}

	/**
	 * 设置属性值
	 * @param fromSource 源对象
	 * @param toTarget 目标对象
	 * @param sourcePropDescriptors 源对象的属性描述数组
	 * @param targetPropDescriptors 目标对象的属性描述数组
	 */
	private static void setProperties(Object fromSource, Object toTarget,
									  PropertyDescriptor[] sourcePropDescriptors,
									  PropertyDescriptor[] targetPropDescriptors, boolean isIgnoreNullValue) {
		if(null == sourcePropDescriptors || null == targetPropDescriptors) {
			return;
		}
		try {
			for (PropertyDescriptor pd : sourcePropDescriptors) {
				PropertyDescriptor targetPd = getTargetPropertyDescriptor(targetPropDescriptors, pd);
				if(null != targetPd) {
					Method readMethod = pd.getReadMethod();
					if(null != readMethod) {
						Object value = readMethod.invoke(fromSource);
						if(isIgnoreNullValue) {
							if(null != value) {
								setPropertyValue(toTarget, pd, targetPd, value);
							}
						} else {
							setPropertyValue(toTarget, pd, targetPd, value);
						}
					}
				}
			}
		} catch(Exception ex){
			LoggerUtils.error(logger, ex);
		}
	}

	/**
	 * 获取属性的读取方法
	 * @param propertyDescriptors 属性描述数组
	 * @param propertyName 属性名称
	 * @return 返回属性读取方法
	 */
	public static Method getReadMethod(PropertyDescriptor[] propertyDescriptors, String propertyName) {
		PropertyDescriptor propertyDescriptor = getPropertyDescriptor(propertyDescriptors, propertyName);
		if(null != propertyDescriptor) {
			return propertyDescriptor.getReadMethod();
		}
		return null;
	}

	/**
	 * 获取属性描述对象
	 * @param propertyDescriptors 属性描述数组
	 * @param propertyName 属性名称
	 * @return 返回属性描述对象
	 */
	public static PropertyDescriptor getPropertyDescriptor(PropertyDescriptor[] propertyDescriptors, String propertyName) {
		return Arrays.stream(propertyDescriptors).filter(pd -> {
			String fieldName = pd.getName();
			if (propertyName.equals(fieldName)) {
				return null != pd.getReadMethod();
			}
			return false;
		}).findAny().orElse(null);
	}

	/**
	 * 获取属性的读取方法
	 * @param propertyDescriptors 属性描述数组
	 * @param propertyName 属性名称
	 * @return 返回属性读取方法
	 */
	public static Method getWriteMethod(PropertyDescriptor[] propertyDescriptors, String propertyName) {
		PropertyDescriptor propertyDescriptor = getPropertyDescriptor(propertyDescriptors, propertyName);
		if(null != propertyDescriptor) {
			return propertyDescriptor.getWriteMethod();
		}
		return null;
	}

	/**
	 * 设置属性的值
	 * @param toTarget 目标对象
	 * @param targetPd 目标属性描述对象
	 * @param value 源属性值
	 * @throws Exception 异常类型
	 */
	private static void setPropertyValue(Object toTarget, PropertyDescriptor sourcePropDesc, PropertyDescriptor targetPd, Object value) throws Exception {
		Method writeMethod = targetPd.getWriteMethod();
		if(null != writeMethod) {
			value = convertValue(sourcePropDesc, targetPd, value);
			LoggerUtils.trace(logger, "源字段名称为:{}, 目标字段名称为:{}, 对应的值为:{}", sourcePropDesc.getName(), targetPd.getName(), value);
			if(null != value) {
				Class<?> valueClass = value.getClass();
				String valueClassName = value.getClass().getName();
				Class<?> targetClass = writeMethod.getParameterTypes()[0];
				String targetClassName = writeMethod.getParameterTypes()[0].getName();
				LoggerUtils.trace(logger, "源字段类型为:{}, 目标字段类型为:{}", valueClassName, targetClassName);
				if(valueClassName.equals(targetClassName)
						|| targetClass.isAssignableFrom(valueClass)
						|| valueClass.isAssignableFrom(targetClass)) {
					try {
						writeMethod.invoke(toTarget, value);
					} catch (Exception e) {
						LoggerUtils.error(logger, e);
					}
				}
			}
		}
	}

	private static Object convertValue(PropertyDescriptor sourcePropDesc, PropertyDescriptor targetPropDesc, Object value) {
		if(StringUtils.isEmpty(value)) {
			return value;
		}
		Method pdMethod = sourcePropDesc.getWriteMethod();
		if(null == pdMethod) {
			return value;
		}
		Class<?> sourceReturnClazz = sourcePropDesc.getReadMethod().getReturnType();
		if(null != targetPropDesc) {
			Class<?> targetReturnClazz = targetPropDesc.getReadMethod().getReturnType();
			String targetReturnClassName = targetReturnClazz.getName();
			LoggerUtils.trace(logger, "源字段类型为:{}, 目标字段类型为:{}",  sourceReturnClazz.getName(), targetReturnClassName);
			if(!sourceReturnClazz.getName().equals(targetReturnClazz.getName())) {
				String sourceValue = StringUtils.handleNull(value);
				String format = IMixConstant.DEFAULT_DATE_TIME_FORMAT;
				if(targetReturnClassName.endsWith(".Date") || targetReturnClassName.endsWith(".LocalDateTime")) {
					if(sourceValue.matches("\\d{4}-\\d{2}-\\d{2}")) {
						format = IMixConstant.DEFAULT_DATE_FORMAT;
					} else if(sourceValue.matches("\\d{8}")) {
						format = IMixConstant.CONTINUOUS_DATE_FORMAT;
					} else if(sourceValue.matches("\\d{14}")) {
						format = IMixConstant.CONTINUOUS_DATE_TIME_FORMAT;
					}
					if(targetReturnClassName.endsWith(".Date")) {
						return DateUtils.parseDate(sourceValue, format);
					} else if(targetReturnClassName.endsWith(".LocalDateTime")) {
						return DateUtils.parseLocalDateTime(sourceValue, format);
					}
				} else if(sourceReturnClazz.getName().endsWith(".Date")) {
					if(targetReturnClassName.endsWith(".Long")) {
						return DateUtils.convertTime((Date) value);
					}
					if(targetReturnClassName.endsWith(".String")) {
						return DateUtils.dateTimeToStr((Date) value);
					}
					return value;
				} else if(sourceReturnClazz.getName().endsWith(".LocalDateTime")) {
					if(targetReturnClassName.endsWith(".Long")) {
						return DateUtils.convertTime((LocalDateTime) value);
					}
					if(targetReturnClassName.endsWith(".String")) {
						return DateUtils.dateTimeToStr((LocalDateTime) value);
					}
					return value;
				} else {
					if(targetReturnClassName.endsWith(".String")) {
						return StringUtils.handleNull(value);
					}
					if (targetReturnClassName.endsWith(".Long")) {
						return Long.parseLong(StringUtils.handleNumNull(value));
					}
				}
				return value;
			} else {
				return value;
			}
		}
		return value;
	}
}
